import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import mplfinance as mpf
import os
from datetime import datetime

# 设置中文字体，确保图表中的中文能正常显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'WenQuanYi Micro Hei', 'Heiti TC']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

class BYDStockAnalysis:
    def __init__(self, file_path):
        """初始化类，加载数据"""
        self.file_path = file_path
        self.data = None
        self.load_data()
        
    def load_data(self):
        """读取Excel文件并加载数据"""
        try:
            self.data = pd.read_excel(self.file_path)
            # 假设日期列名为'日期'或'Date'，将其转换为datetime类型
            date_columns = ['日期', 'Date']
            for col in date_columns:
                if col in self.data.columns:
                    self.data[col] = pd.to_datetime(self.data[col])
                    self.data = self.data.set_index(col)
                    break
            
            print(f"数据加载成功，数据形状: {self.data.shape}")
        except Exception as e:
            print(f"加载数据时出错: {e}")
    
    def task1(self):
        """任务1：读取文件，观察数据形状，输出前5行，并对定量变量进行描述性分析"""
        print("\n===== 任务1：数据基本信息与描述性分析 =====")
        print(f"数据形状: {self.data.shape}")
        print("\n数据前5行:")
        print(self.data.head())
        
        print("\n数据列信息:")
        print(self.data.info())
        
        print("\n定量变量描述性分析:")
        # 选择数值型列进行描述性统计
        numeric_cols = self.data.select_dtypes(include=[np.number]).columns
        print(self.data[numeric_cols].describe())
        
    def task2(self):
        """任务2：计算变量之间的相关系数，并绘制相关系数热力图"""
        print("\n===== 任务2：相关系数分析与热力图 =====")
        
        # 选择数值型列计算相关系数
        numeric_cols = self.data.select_dtypes(include=[np.number]).columns
        corr_matrix = self.data[numeric_cols].corr()
        
        print("相关系数矩阵:")
        print(corr_matrix)
        
        # 绘制热力图
        plt.figure(figsize=(12, 10))
        sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
        plt.title('比亚迪股票数据变量相关系数热力图')
        plt.tight_layout()
        plt.savefig('correlation_heatmap.png', dpi=300)
        plt.show()
        print("相关系数热力图已保存为 correlation_heatmap.png")
    
    def task3(self):
        """任务3：绘制2023年全年的收盘价时序图"""
        print("\n===== 任务3：2023年收盘价时序图 =====")
        
        # 确保索引是日期类型
        if not pd.api.types.is_datetime64_any_dtype(self.data.index):
            print("索引不是日期类型，请检查数据格式")
            return
        
        # 筛选2023年的数据
        data_2023 = self.data.loc['2023-01-01':'2023-12-31']
        
        # 寻找收盘价列
        close_columns = ['收盘价', 'close', 'Close', '收盘']
        close_col = None
        for col in close_columns:
            if col in data_2023.columns:
                close_col = col
                break
        
        if close_col is None:
            print("未找到收盘价列，请检查数据格式")
            return
        
        # 绘制时序图
        plt.figure(figsize=(14, 7))
        plt.plot(data_2023.index, data_2023[close_col], marker='', linestyle='-', color='blue', label=close_col)
        plt.title('比亚迪2023年收盘价时序图')
        plt.xlabel('日期')
        plt.ylabel('价格')
        plt.grid(True, linestyle='--', alpha=0.7)
        plt.legend()
        plt.tight_layout()
        plt.savefig('2023_close_price.png', dpi=300)
        plt.show()
        print("2023年收盘价时序图已保存为 2023_close_price.png")
    
    def task4(self):
        """任务4：绘制2023年4月到2023年6月的K线图"""
        print("\n===== 任务4：2023年4-6月K线图分析 =====")
        
        # 确保索引是日期类型
        if not pd.api.types.is_datetime64_any_dtype(self.data.index):
            print("索引不是日期类型，请检查数据格式")
            return
        
        # 筛选2023年4月到6月的数据
        data_k = self.data.loc['2023-04-01':'2023-06-30'].copy()
        
        # 寻找OHLC列（开盘价、最高价、最低价、收盘价）
        ohlc_columns = {
            'open': None, 
            'high': None, 
            'low': None, 
            'close': None,
            'volume': None
        }
        
        # 映射中文列名到英文
        column_mapping = {
            '开盘价': 'open', '开盘': 'open', 'Open': 'open',
            '最高价': 'high', '最高': 'high', 'High': 'high',
            '最低价': 'low', '最低': 'low', 'Low': 'low',
            '收盘价': 'close', '收盘': 'close', 'Close': 'close',
            '成交量': 'volume', '成交': 'volume', 'Volume': 'volume'
        }
        
        for col in data_k.columns:
            for key, value in column_mapping.items():
                if key in col:
                    ohlc_columns[value] = col
                    break
        
        # 检查是否找到所有必要的列
        missing_columns = [key for key, value in ohlc_columns.items() if value is None and key != 'volume']
        if missing_columns:
            print(f"未找到必要的列: {missing_columns}")
            return
        
        # 重命名列以便mplfinance使用
        data_k_renamed = data_k.rename(columns={
            ohlc_columns['open']: 'Open',
            ohlc_columns['high']: 'High',
            ohlc_columns['low']: 'Low',
            ohlc_columns['close']: 'Close'
        })
        
        # 如果有成交量列，也重命名
        if ohlc_columns['volume']:
            data_k_renamed = data_k_renamed.rename(columns={ohlc_columns['volume']: 'Volume'})
        
        # 确保只包含mplfinance需要的列
        required_cols = ['Open', 'High', 'Low', 'Close']
        if 'Volume' in data_k_renamed.columns:
            required_cols.append('Volume')
        data_k_renamed = data_k_renamed[required_cols]
        
        # 绘制K线图
        mpf_style = mpf.make_mpf_style(base_mpf_style='charles', rc={'font.family': ['SimHei']})
        
        fig, axes = mpf.plot(
            data_k_renamed, 
            type='candle', 
            style=mpf_style,
            title='比亚迪2023年4-6月K线图',
            ylabel='价格',
            volume=('Volume' in data_k_renamed.columns),
            ylabel_lower='成交量',
            figsize=(14, 10),
            tight_layout=True,
            returnfig=True
        )
        
        # 添加网格线
        axes[0].grid(True, linestyle='--', alpha=0.7)
        if len(axes) > 1:
            axes[1].grid(True, linestyle='--', alpha=0.7)
        
        plt.savefig('2023_apr_jun_kline.png', dpi=300)
        plt.show()
        
        # K线图分析
        print("\nK线图分析:")
        print(f"分析时间段: 2023年4月1日至2023年6月30日")
        print(f"期间开盘价: {data_k_renamed['Open'].iloc[0]:.2f}")
        print(f"期间收盘价: {data_k_renamed['Close'].iloc[-1]:.2f}")
        print(f"期间最高价: {data_k_renamed['High'].max():.2f}")
        print(f"期间最低价: {data_k_renamed['Low'].min():.2f}")
        
        # 计算涨跌情况
        start_price = data_k_renamed['Open'].iloc[0]
        end_price = data_k_renamed['Close'].iloc[-1]
        change_percent = (end_price - start_price) / start_price * 100
        print(f"期间涨跌幅: {change_percent:.2f}%")
        
        print("\nK线形态简要分析:")
        if change_percent > 5:
            print("1. 整体趋势：上涨趋势明显，期间涨幅超过5%")
        elif change_percent < -5:
            print("1. 整体趋势：下跌趋势明显，期间跌幅超过5%")
        else:
            print("1. 整体趋势：区间震荡，价格波动相对平稳")
        
        # 计算波动幅度
        volatility = (data_k_renamed['High'] - data_k_renamed['Low']).mean()
        avg_price = data_k_renamed['Close'].mean()
        volatility_percent = (volatility / avg_price) * 100
        print(f"2. 平均日波动幅度: {volatility_percent:.2f}%")
        
        # 成交量分析
        if 'Volume' in data_k_renamed.columns:
            avg_volume = data_k_renamed['Volume'].mean()
            max_volume = data_k_renamed['Volume'].max()
            print(f"3. 平均成交量: {avg_volume:.0f}")
            print(f"4. 最大成交量: {max_volume:.0f}")
            
            # 找出成交量最大的日期
            max_volume_date = data_k_renamed['Volume'].idxmax().strftime('%Y-%m-%d')
            print(f"   成交量最大日期: {max_volume_date}")
        
        print("\n2023年4-6月K线图已保存为 2023_apr_jun_kline.png")
    
    def run_all_tasks(self):
        """运行所有任务"""
        self.task1()
        self.task2()
        self.task3()
        self.task4()

if __name__ == "__main__":
    # 获取当前目录下的Excel文件
    current_dir = os.path.dirname(os.path.abspath(__file__))
    file_name = "比亚迪后复权历史行情数据.xlsx"
    file_path = os.path.join(current_dir, file_name)
    
    # 检查文件是否存在
    if not os.path.exists(file_path):
        print(f"未找到文件: {file_path}")
        # 尝试直接使用提供的路径
        file_path = "c:\\Users\\YEDX\\Desktop\\新建文件夹\\比亚迪后复权历史行情数据.xlsx"
        if not os.path.exists(file_path):
            print(f"仍然未找到文件: {file_path}")
            exit(1)
    
    # 创建分析对象并运行所有任务
    analyzer = BYDStockAnalysis(file_path)
    analyzer.run_all_tasks()
    
    print("\n===== 比亚迪股票价格走势分析与可视化任务已完成 =====")